--these are functions for doing collision detection

--unless one wants to write their own routines, then the
-- collide_Object_bounds() and collide_Object_Rects() shouldn't be used
--
-- Instead, please use the check_collisions_*() functions

--returns the dot product of two vectors
function vec_dot(a, b)
  result = 0
  for i = 1, #a, 1 do
    result = result + a[i]*b[i]
  end
  return result
end

--returns vector mult by a scalar
function vec_scale(vec, s)
  nv = {}
  for i = 1, #vec, 1 do
    nv[i] = vec[i]*s
  end
  return nv
end

--rotate vector
function vec_rot(vec, angle)
  r_angle = math.rad(angle)
  return {vec[1]*math.cos(r_angle)-vec[2]*math.sin(-1*r_angle), vec[1]*math.sin(-1*r_angle)+vec[2]*math.cos(r_angle)}
end

--magnitude of vector
function vec_mag(vec)
  mag = 0
  for i = 1, #vec, 1 do
    mag = mag + (vec[i]*vec[i])
  end
  return math.sqrt(mag)
end

--normalize vector
function vec_norm(vec)
  mag = vec_mag(vec)
  new_vec = {}
  for i = 1, #vec, 1 do
    new_vec[i] = vec[i]/mag
  end
  
  return new_vec 
end

--transforms an object's axes into vectors.
--  returns a pair of vectors.
function ____create_axes(angle)
  return vec_rot({1,0},angle), vec_rot({0,1},angle)
end

function collide_Object_bounds(a, b)
  --this is provided as a convenience so that one can make objects invisible to collisions
  if a.no_collide == true or b.no_collide == true then return false end

  --the rotated axes that we project onto
  local vecAx, vecAy = ____create_axes(a.rotation)
  local vecBx, vecBy = ____create_axes(b.rotation)
  
  local rAx = a.w/2.0  local rAy = a.h/2.0
  local rBx = b.w/2.0  local rBy = b.h/2.0
  
  --displacement vectors
  local a_x, a_y = a:get_center()
  a_x = a_x - a.x_offset   a_y = a_y - a.y_offset
  local b_x, b_y = b:get_center()
  b_x = b_x - b.x_offset   b_y = b_y - b.y_offset
  local vec_dispA = {a_x-b_x, a_y-b_y}
  local vec_dispB = {b_x-a_x, b_y-a_y}
  
  --do tests
  --Ax
  local rb = math.abs(rBx*vec_dot(vecBx,vecAx))+math.abs(rBy*vec_dot(vecBy,vecAx))
  if math.abs(vec_dot(vec_dispB, vecAx)) > rAx+rb then return false end
  
  --Ay
  rb = math.abs(rBx*vec_dot(vecBx,vecAy))+math.abs(rBy*vec_dot(vecBy,vecAy))
  if math.abs(vec_dot(vec_dispB, vecAy)) > rAy+rb then return false end
  
  --Bx
  local ra = math.abs(rAx*vec_dot(vecAx,vecBx))+math.abs(rAy*vec_dot(vecAy,vecBx))
  if math.abs(vec_dot(vec_dispA, vecBx)) > ra+rBx then return false end
  
  --By
  ra = math.abs(rAx*vec_dot(vecAx,vecBy))+math.abs(rAy*vec_dot(vecAy,vecBy))
  if math.abs(vec_dot(vec_dispA, vecBy)) > ra+rBy then return false end
  
  return true
end


function collide_Object_Rects(a, b)
  if a.no_collide or b.no_collide then return false end

  local a_ids = ObjectList:new()
  local b_ids = ObjectList:new()

  local vecAx, vecAy = ____create_axes(a.rotation)
  local vecBx, vecBy = ____create_axes(b.rotation)

  for r1 in a.rects:iterator() do
    for r2 in b.rects:iterator() do
      
      local flag = true
      
      --the rotated axes that we project onto
  
      local rAx = r1.w/2  local rAy = r1.h/2
      local rBx = r1.w/2  local rBy = r1.h/2
  
      --displacement vectors
      local a_x = r1.x + rAx - (a.w/2)
      local a_y = r1.y + rAy - (a.h/2) 
      local b_x = r2.x + rBx - (b.w/2)
      local b_y = r2.y + rBy - (a.h/2)
      
      local a_xr = a_x*math.cos(a.rotation) - a_y*math.sin(a.rotation)
      local a_yr = a_x*math.sin(a.rotation) + a_y*math.cos(a.rotation)
      local b_xr = b_x*math.cos(b.rotation) - b_y*math.sin(b.rotation)
      local b_yr = b_x*math.sin(b.rotation) + b_y*math.cos(b.rotation)
      
      a_x = a_xr + a.x + (a.w/2) - a.x_offset
      a_y = a_yr + a.y + (a.h/2) - a.y_offset
      b_x = b_xr + b.x + (b.w/2) - b.x_offset
      b_y = b_yr + b.y + (b.h/2) - b.y_offset
      
      local vec_dispA = {a_x-b_x, a_y-b_y}
      local vec_dispB = {b_x-a_x, b_y-a_y}
  
      --do tests
      --Ax
      local rb = math.abs(rBx*vec_dot(vecBx,vecAx))+math.abs(rBy*vec_dot(vecBy,vecAx))
      if math.abs(vec_dot(vec_dispB, vecAx)) > rAx+rb then flag = false end
  
      --Ay
      rb = math.abs(rBx*vec_dot(vecBx,vecAy))+math.abs(rBy*vec_dot(vecBy,vecAy))
      if math.abs(vec_dot(vec_dispB, vecAy)) > rAy+rb then flag = false end
  
      --Bx
      local ra = math.abs(rAx*vec_dot(vecAx,vecBx))+math.abs(rAy*vec_dot(vecAy,vecBx))
      if math.abs(vec_dot(vec_dispA, vecBx)) > ra+rBx then flag = false end
  
      --By
      ra = math.abs(rAx*vec_dot(vecAx,vecBy))+math.abs(rAy*vec_dot(vecAy,vecBy))
      if math.abs(vec_dot(vec_dispA, vecBy)) > ra+rBy then flag = false end

	  if flag == true then
        if a_ids:exists(r1.id) == false then a_ids:push_back(r1.id) end
        if b_ids:exists(r2.id) == false then b_ids:push_back(r2.id) end
      end

    end
  end

  return a_ids, b_ids
end



--checks for collisions between two lists and calls the collide() function of each
function check_collisions_lists(list1, list2)
  local id1, id2
  for obj1 in list1:iterator() do
    for obj2 in list2:iterator() do
      if obj1 ~= obj2 and collide_Object_bounds(obj1, obj2) then
        id1, id2 = collide_Object_Rects(obj1, obj2)
        obj1:collide(id1, obj2)
        obj2:collide(id2, obj1)
      end
    end
  end
end

--checks for collisions between an object and a list
function check_collisions_obj_list(obj, list2)
  local id1, id2
  for obj2 in list2:iterator() do
    if obj ~= obj2 and collide_Object_bounds(obj, obj2) then
      id1, id2 = collide_Object_Rects(obj, obj2)
      obj:collide(id1, obj2)
      obj2:collide(id2, obj)
    end
  end
end

--checks for collisions between two objects
function check_collisions_obj_obj(obj1, obj2)
  local id1, id2
  if obj1 ~= obj2 and collide_Object_bounds(obj1, obj2) then
    id1, id2 = collide_Object_Rects(obj1, obj2)
    obj1:collide(id1, obj1)
    obj2:collide(id2, obj2)
  end
end

--checks for collisions between an object and a list. returns true or false.
function check_collisions_any(obj, list)
  local id1, id2
  for obj2 in list2:iterator() do
    if collide_Object_bounds(obj, obj2) then
      if obj.rects.size ~= 0 and obj2.rects.size ~= 0 then
        id1, id2 = collide_Object_Rects(obj, obj2)
        if id1.size > 0 and id2.size > 0 then
          return true
        else
          return false
        end
      else
        return true
      end
    end
  end
end
